#pragma once
#include <hiredis/hiredis.h>
#include "Singleton.h"
#include <string>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <queue>
#include <iostream>
//封装redis连接池
class RedisConPool
{
public:
    RedisConPool(std::size_t poolSize,const char* host,int port,const char* password)
    :poolSize_(poolSize),host_(host),port_(port),b_stop_(false)
    {
        for(int i=0;i<poolSize_;++i)
        {
            redisContext * ctx = redisConnect(host_,port_);
            if(ctx==NULL||ctx->err)
            {
                
                printf("connect redis failed, err:%s\n", ctx->errstr);
                continue;
            }
            //认证
            redisReply * reply = (redisReply *)redisCommand(ctx, "AUTH %s", password);
            if (reply->type == REDIS_REPLY_ERROR)
            {
                redisFree(ctx);
                printf("auth redis failed, err:%s\n", reply->str);
                continue;
            }
            freeReplyObject(reply);
            std::cout<<"认证成功"<<std::endl;
            connections_.push(ctx);
        }
    }
    ~RedisConPool()
    {
        {

            std::unique_lock<std::mutex> lock(mtx_);
            Close();
            while (!connections_.empty())
            {
                redisContext * ctx = connections_.front();
                connections_.pop();
                redisFree(ctx);
            }
        }
        std::cout<<"RedisConPool::~RedisConPool()"<<std::endl;
    }
    void Close()
    {
        
        b_stop_=true;
        cond_.notify_all();
    }

    //获取连接
    redisContext* GetConnection()
    {
        std::unique_lock<std::mutex> lock(mtx_);
        cond_.wait(lock,[this](){
            return !connections_.empty() || b_stop_;
        });
        if (b_stop_)
        {
            return nullptr;
        }
        auto ctx = connections_.front();
        connections_.pop();
        return ctx;
        
    }
    //归还连接
    void ReturnConnection(redisContext* ctx)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        if (b_stop_)
        {
            return;
        }
        
        connections_.push(ctx);
        cond_.notify_one();
    }

private:
    std::size_t poolSize_;
    std::mutex mtx_;
    std::queue<redisContext *> connections_;
    std::condition_variable cond_;
    bool b_stop_;
    const char * host_;
    int port_;


};




class RedisMgr: public Singleton<RedisMgr>
{
    friend class Singleton<RedisMgr>;
public:
    ~RedisMgr();
    bool Get(const std::string &key, std::string& value);
    bool Set(const std::string &key, const std::string &value);

    bool LPush(const std::string &key, const std::string &value);
    bool LPop(const std::string &key, std::string& value);
    bool RPush(const std::string& key, const std::string& value);
    bool RPop(const std::string& key, std::string& value);
    bool HSet(const std::string &key, const std::string  &hkey, const std::string &value);
    bool HSet(const char* key, const char* hkey, const char* hvalue, size_t hvaluelen);
    std::string HGet(const std::string &key, const std::string &hkey);
    bool Del(const std::string &key);
    bool ExistsKey(const std::string &key);
    
    bool HDel(const std::string &key, const std::string &hkey);



private:
    RedisMgr();
    redisContext* connect_;
    redisReply* reply_;
    std::unique_ptr<RedisConPool> pool_;
};
